######################################################################################
#
# This file was provided as part of the SharePoint Mystery Deployment package
# Created by Christopher Kimbell (http://sharepointmystery.com)
#
######################################################################################

# -----------------------------------------------------------------
# Creates an object used to manipulate the User Profile Service
# -----------------------------------------------------------------
function New-UserProfileService()
{
    $obj=new-object PSObject -Property @{
      ProfileDB = New-Database;
      SocialDB = New-Database;
      SyncDB = New-Database;
      Pool = New-ApplicationPool;
      Service = New-SupportingService;
      Name = "User Profile Service";
      Proxy = "User Profile Service Proxy";
      NetBIOSDomainNamesEnabled = $false;
      EnableActivityFeed = $true;
      SiteNamingConflictResolution = "None";
    }
    $obj.Service.Name = "User Profile Service"
    # -----------------------------------------------------------------
    # Validates the information in the object
    # -----------------------------------------------------------------
    Add-Method $obj "Validate" `
    {
        Write-Operation "Validating User Profile Service"
        $this.Pool.Validate()
        $this.ProfileDB.Validate()
        $this.SocialDB.Validate()
        $this.SyncDB.Validate()
        $this.Service.Validate()
        
        Validate-NotEmpty "Name" $this.Name
        Validate-NotEmpty "Proxy" $this.Proxy 
        Validate-MemberOf "SiteNamingConflictResolution" $this.SiteNamingConflictResolution "None","Resolve","Block"
    }
    # -----------------------------------------------------------------
    # Provisions the Service
    # -----------------------------------------------------------------
    Add-Method $obj "Provision" `
    {
        $sa = Get-SPServiceApplication  | Where-Object {$_.DisplayName -eq $this.Name}

        if($sa -eq $null)
        {
            Write-Operation "Creating $($this.Name)"
            $pool = $this.Pool.Provision()
            
            $farm.ResolveCredentials()
           
            $this.ProfileDB.Provision()
            $this.ProfileDB.AddRole($farm.DeploymentAccount, "db_owner")
            $this.ProfileDB.AddRole($this.Pool.Account, "db_owner")
            $this.SocialDB.Provision()
            $this.SocialDB.AddRole($farm.DeploymentAccount, "db_owner")
            $this.SocialDB.AddRole($this.Pool.Account, "db_owner")
            $this.SyncDB.Provision()
            $this.SyncDB.AddRole($farm.DeploymentAccount, "db_owner")
            $this.SyncDB.AddRole($this.Pool.Account, "db_owner")
     
			# when we create the database running as farm account, the farm account becomes the dbo user
			# this seems to be enough to get the user profile synchronization service to start
			# even if the actual service is provisioned as the running user.
			# if this doesn't seem to be the case in some scenarios, include alternative code as well
            
            # http://harbar.net/archive/2010/10/30/avoiding-the-default-schema-issue-when-creating-the-user-profile.aspx
            $impersonate = $false
            
			Write-Operation "Provisioning service. This may take a looooooooong time"
            if($impersonate -eq $true)
            {
                $sb = 
                {   
                    param($poolName, $serviceName, $profileServer, $profileName, $socialServer, $socialName, $syncServer, $syncName, $hostLocation, $resolution)      
                    Add-PSSnapin Microsoft.SharePoint.PowerShell          
                    $saAppPool = Get-SPServiceApplicationPool $poolName         
                    $upa = New-SPProfileServiceApplication -Name $serviceName `
                                                           -ApplicationPool $saAppPool `
                                                           -MySiteHostLocation $hostLocation `
                                                           -ProfileDBServer $profileServer `
                                                           -ProfileDBName $profileName `
                                                           -SocialDBServer $socialServer `
                                                           -SocialDBName $socialName `
                                                           -ProfileSyncDBServer $syncServer `
                                                           -ProfileSyncDBName $syncName `
                                                           -SiteNamingConflictResolution $resolution
                } 
                
                $job = Start-Job -Credential $farm.TimerAccountCredentials `
                                 -ScriptBlock $sb `
                                 -ArgumentList $this.Pool.Name, $this.Name, $this.ProfileDB.Server, $this.ProfileDB.FullName, $this.SocialDB.Server, $this.SocialDB.FullName, $this.SyncDB.Server, $this.SyncDB.FullName, $mySiteApp.DefaultZone.AbsoluteUrl, $this.SiteNamingConflictResolution
                Wait-Job $job
            }
            else
            {
                $sa = New-SPProfileServiceApplication -Name $this.Name `
                        -ApplicationPool $pool `
                        -MySiteHostLocation $mySiteApp.DefaultZone.AbsoluteUrl `
                        -ProfileDBServer $this.ProfileDB.Server `
                        -ProfileDBName $this.ProfileDB.FullName `
                        -SocialDBServer $this.SocialDB.Server `
                        -SocialDBName $this.SocialDB.FullName `
                        -ProfileSyncDBServer $this.SyncDB.Server `
                        -ProfileSyncDBName $this.SyncDB.FullName `
                        -SiteNamingConflictResolution $this.SiteNamingConflictResolution
            }
        }
        else
        {
            Write-OperationDone "$($this.Name) has already been provisioned"  
        } 
        # need to get a new reference in case the application was just created
        $sa = Get-SPServiceApplication  | Where-Object {$_.DisplayName -eq $this.Name}
        
        if($this.NetBIOSDomainNamesEnabled -ne $sa.NetBIOSDomainNamesEnabled)
        {
            $sa.NetBIOSDomainNamesEnabled = $netBios
            $sa.Update()
        } 
                       
        # make sure that the running user and deployment account has permissions to configure the service
		Grant-Permission $sa $runningUser "Full Control" $true
		if($runningUser -ne $farm.DeploymentAccount)
		{
			Grant-Permission $sa $farm.DeploymentAccount "Full Control" $true
		}
		Grant-Permission $sa $searchSvc.ContentAccessAccount "Retrieve People Data for Search Crawlers" $true
				 		 
        $proxy = Get-SPServiceApplicationProxy  | Where-Object {$_.DisplayName -eq $this.Proxy}  
        if($proxy -eq $null)
        {
            New-SPProfileServiceApplicationProxy -Name $this.Proxy `
            -ServiceApplication $sa `
            -DefaultProxyGroup
        }
        else
        {
            Write-OperationDone "$($this.Proxy) has already been provisioned"  
        }
        $this.Service.Provision()
        
        if($this.EnableActivityFeed -eq $true)
        {
            Enable-TimerJob "ActivityFeedJob"
        }
        
        return $sa
    }
    # -----------------------------------------------------------------
    # Unprovisions the Service
    # -----------------------------------------------------------------
    Add-Method $obj "Unprovision" `
    {
        Remove-ServiceApplicationProxy $this.Proxy
        Remove-ServiceApplication $this.Name
        $this.Service.Unprovision()
    }
    return $obj
}
